module FdEnv where

import FdType
import FdUtil
import FdHelper

import Data.List
import Data.IORef
import Monad
import Control.Monad.Error

--

-------------------------------------------------------
-- # about env; mutable vars
-------------------------------------------------------

-- type Env = IORef [(Id, IORef Val)] 

makeEnv :: IO Env
makeEnv = newIORef []

cloneEnv :: Env -> IO Env
cloneEnv env = do {
	env <- readIORef env;
	newIORef env;
}

isDefined :: Env -> Id -> IO Bool
isDefined env id = do {
	vars <- readIORef env;
	case lookup id vars of
		Nothing -> return True;
		Just _  -> return False;
}

getVar :: Env -> Id -> Fineday Val
getVar envRef id = do {
	varRef <- getVarRef envRef id;
	io $ readIORef varRef;
}

getVarRef :: Env -> Id -> Fineday (IORef Val)
getVarRef envRef id = do {
	env <- io $ readIORef envRef;
	case lookup id env of 
		Just valRef ->
			return valRef;
		Nothing ->
			throwError $ NameErr id
}

defineVar :: Env -> (Id, Val) -> Fineday Val 
defineVar envRef (id, val) = do {
	env <- io $ readIORef envRef;
	env <- return $ filter (\(_id, _) -> _id/=id) env; -- clear the current scope
	valRef <- io $ newIORef val;
	io $ writeIORef envRef $ ((id, valRef):env);
	return val;
}

defineVars :: Env -> [(Id, Val)] -> Fineday Val
defineVars envRef pairs =
	liftM last $ mapM (defineVar envRef) pairs

	--monad sucks!
setVar :: Env -> (Id, Val) -> Fineday Val
setVar envRef (id, val) = do {
	env <- io $ readIORef envRef;
	(case lookup id env of
		Just valRef -> do {
			io $ writeIORef valRef val;
		}
		Nothing -> do {
			--valRef <- io $ newIORef val;
			--io $ writeIORef envRef $ ((id, valRef):env);	
			throwError $ NameErr $ id;
		});
	return val;
}

setVarByRef :: Env -> (IORef Val) -> Val -> Fineday Val
setVarByRef envRef varRef val = do {
	io $ writeIORef varRef val;
	return val;
}

setVars :: Env -> [(Id, Val)] -> Fineday Val
setVars envRef pairs = 
	liftM last $ mapM (setVar envRef) pairs


modifyVar :: Env -> Id -> (Val -> Val) -> Fineday Val
modifyVar env id func = do {
	var <- getVar env id;
	setVar env (id, (func var));
	return var;
}


loadFunctions :: Env -> [(Id, Func)] -> Fineday Val
loadFunctions env funcs = defineVars env $ map (\(fn, f) -> (fn, (Function f))) funcs

